﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Globalization;

namespace Admx_Generator
{
    public class AdmlFile
    {
        private Revision _revision = new Revision(1, 0);
        private CultureInfo _culture;
        private Dictionary<string, string> _stringTable = new Dictionary<string, string>();
        private Dictionary<string, IPresentationElement> _presentationTable = new Dictionary<string, IPresentationElement>();

        /// <summary>
        /// Create a new instance of the AdmlFile class for this Culture.
        /// </summary>
        /// <param name="cultureName">The name of the culture associated with this AdmlFile. For example "fr-FR" or "RU".</param>
        public AdmlFile(string cultureName)
        {
            this._culture = new System.Globalization.CultureInfo(cultureName);
        }

        #region (internal properties)

        /// <summary>
        /// Gets the version of the file.
        /// </summary>
        public Revision Revision
        {
            get { return this._revision; }
            set
            {
                if (!this._revision.Equals(value))
                {
                    this._revision = value;
                    if (this.OnChanged != null)
                        this.OnChanged(this);
                }
            }
        }

        /// <summary>
        /// Gets or Sets the Culture of this AdmlFile.
        /// </summary>
        public CultureInfo Culture
        {
            get { return new CultureInfo(this._culture.Name); }
            private set { this._culture = value; }
        }

        #endregion (internal properties)

        #region {methods}

        /// <summary>
        /// Return the string associated to the provided key from the StringTable.
        /// </summary>
        /// <param name="stringId">Unique id of the string to retrieve.</param>
        /// <returns>The string associated to the provided key, or String.Empty if no string is associated.</returns>
        public string GetString(string stringId)
        {
            return this._stringTable.ContainsKey(stringId) ? this._stringTable[stringId] : String.Empty;
        }

        /// <summary>
        /// Modify the string associated to a string id into the StringTable.
        /// </summary>
        /// <param name="stringId">Id of the string to modify. If the stringId is not found, it is create.</param>
        /// <param name="newValue">The new string to store.</param>
        public void ModifyString(string stringId, string newValue)
        {
            if (!this._stringTable.ContainsKey(stringId))
            {
                this._stringTable.Add(stringId, newValue);
                if (this.OnChanged != null)
                    this.OnChanged(this);
            }
            else
            {
                this._stringTable[stringId] = newValue;
                if (this.OnChanged != null)
                    this.OnChanged(this);
            }
        }

        /// <summary>
        /// Delete the string associated with the provided stringId from the StringTable.
        /// </summary>
        /// <param name="stringId">Unique Id of the string into the StringTable.</param>
        public void DeleteString(string stringId)
        {
            this._stringTable.Remove(stringId);
            if (this.OnChanged != null)
                this.OnChanged(this);
        }

        /// <summary>
        /// Check if a string exists.
        /// </summary>
        /// <param name="stringId">Id of the string.</param>
        /// <returns></returns>
        public bool StringExist(string stringId)
        {
            return this._stringTable.ContainsKey(stringId);
        }

        /// <summary>
        /// Return the Presentation element associated to the provided key from the PresentationTable.
        /// </summary>
        /// <param name="presentationId">Unique id of the Presentation element to retrieve.</param>
        /// <returns>The Presentation element associated to the provided key, or null if no presentation is associated with this id.</returns>
        public IPresentationElement GetPresentation(string presentationId)
        {
            return this._presentationTable.ContainsKey(presentationId) ? this._presentationTable[presentationId] : null;
        }

        /// <summary>
        /// Modify the Presentation element associated to a presentationId into the PresentationTable.
        /// </summary>
        /// <param name="presentationId">Id of the Presentation element to modify. If the presentationId is not found, it is create.</param>
        /// <param name="newValue">The new Presentation element to store.</param>
        public void ModifyPresentation(string presentationId, IPresentationElement newValue)
        {
            if (!this._presentationTable.ContainsKey(presentationId))
            {
                this._presentationTable.Add(presentationId, newValue);
                if (this.OnChanged != null)
                    this.OnChanged(this);
            }
            else
            {
                this._presentationTable[presentationId] = newValue;
                if (this.OnChanged != null)
                    this.OnChanged(this);
            }

        }

        /// <summary>
        /// Delete the Presentation element associated with the provided presentationId from the PresentationTable.
        /// </summary>
        /// <param name="presentationId">Unique Id of the Presentation element into the PresentationTable.</param>
        public void DeletePresentation(string presentationId)
        {
            this._presentationTable.Remove(presentationId);
            if (this.OnChanged != null)
                this.OnChanged(this);
        }

        /// <summary>
        /// Check if presentation exists.
        /// </summary>
        /// <param name="presentationId">Id of the presentation to search.</param>
        /// <returns></returns>
        public bool PresentationExists(string presentationId)
        {
            return this._presentationTable.ContainsKey(presentationId);
        }

        /// <summary>
        /// Save the Adml file to the disk as a yml file.
        /// </summary>
        /// <param name="path">Path to the folder where to write the Adml file.</param>
        /// <param name="filename">Name of the file without extension.</param>
        public void Save(string path, string filename)
        {
            XmlWriterSettings writerSettings = new XmlWriterSettings();
            writerSettings.Encoding = Encoding.UTF8;
            writerSettings.Indent = true;
            FileInfo admlFile = new FileInfo(Path.Combine(path, filename + ".adml"));
            StreamWriter writer = admlFile.CreateText();
            
            XmlWriter xmlWriter = XmlWriter.Create(writer, writerSettings);

            xmlWriter.WriteStartDocument();

            // policyDefinitionResources
            xmlWriter.WriteStartElement("policyDefinitionResources", @"http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions");
            xmlWriter.WriteAttributeString("xsd", XNamespace.Xmlns.ToString(), @"http://www.w3.org/2001/XMLSchema");
            xmlWriter.WriteAttributeString("xsi", XNamespace.Xmlns.ToString(), @"http://www.w3.org/2001/XmlSchema-instance");
            xmlWriter.WriteAttributeString("revision", this.Revision.ToString());
            xmlWriter.WriteAttributeString("schemaVersion", "1.0");

            // resources
            xmlWriter.WriteStartElement("resources");

            // stringTable
            if (this._stringTable.Count != 0)
            {
                xmlWriter.WriteStartElement("stringTable");
                foreach (KeyValuePair<string, string> stringItem in this._stringTable)
                {
                    xmlWriter.WriteStartElement("string");
                    xmlWriter.WriteAttributeString("id", stringItem.Key);
                    xmlWriter.WriteString(stringItem.Value);
                    xmlWriter.WriteEndElement();
                }
                xmlWriter.WriteEndElement(); // stringTable
            }

            // presentationTable
            xmlWriter.WriteStartElement("presentationTable");
            foreach (KeyValuePair<string, IPresentationElement> presentationItem in this._presentationTable)
            {
                // presentation
                xmlWriter.WriteStartElement("presentation");
                xmlWriter.WriteAttributeString("id", presentationItem.Key);
                presentationItem.Value.Save(xmlWriter);
                xmlWriter.WriteEndElement(); // presentation
            }
            xmlWriter.WriteEndElement(); // presentationTable
            xmlWriter.WriteEndElement(); // resources
            xmlWriter.WriteEndElement(); // policyDefinitionResources
            xmlWriter.WriteEndDocument();

            xmlWriter.Flush();
            xmlWriter.Close();
            writer.Flush();
            writer.Close();
        }

        /// <summary>
        /// Delete all informations related to the associated Admx
        /// </summary>
        public void Reset()
        {
            this._revision = new Revision(1, 0);
            this._stringTable.Clear();
            this._presentationTable.Clear();
            if (this.OnChanged != null)
                this.OnChanged(this);
        }

        public override string ToString()
        {
            return this.Culture.DisplayName;
        }

        #endregion {methods}

        #region (public events)

        public delegate void OnChangeEventHandler(AdmlFile sender);

        /// <summary>
        /// This event is fire each time a property is changed.
        /// </summary>
        public event OnChangeEventHandler OnChanged;

        #endregion (public events)
    }
}
